package com.cgs.query.handler.sync

import com.cgs.query.domain.ExecutionObj
import com.cgs.query.domain.QueryDefine
import com.cgs.query.domain.SyncProject
import com.cgs.query.exception.QueryException
import com.cgs.query.handler.*
import com.cgs.query.handler.SyncHandler.Companion.PROJECT_SYNC_METHOD_NAME
import com.cgs.query.service.IExecutionObjService
import com.cgs.query.service.IProject2DefineService
import com.cgs.query.service.IQueryDefineService
import com.cgs.query.service.IQueryProjectService
import com.cgs.query.util.Utils
import kotlinx.coroutines.experimental.async
import kotlinx.coroutines.experimental.runBlocking
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.transaction.annotation.Transactional
import java.util.concurrent.atomic.AtomicLong

/**
 * 远程同步处理器
 */
open class RemoteProjectSyncHandler : SyncHandler, UserInfoHolderAware {

    @Autowired
    private lateinit var remoteHandler: RemoteHandler
    @Autowired
    private lateinit var projectService: IQueryProjectService
    @Autowired
    private lateinit var defineService: IQueryDefineService
    @Autowired
    private lateinit var executionObjService: IExecutionObjService
    @Autowired
    private lateinit var project2DefineService: IProject2DefineService

    private lateinit var userInfoHolder: UserInfoHolder

    private val sequence = AtomicLong(1)

    @Transactional
    override fun targetServerReceive(serverUrl: String, targetProject: String, originProject: String) {
        val request = Request(sequence.getAndIncrement(), PROJECT_SYNC_METHOD_NAME, listOf(originProject), SyncProject::class.java)
        val response = remoteHandler.transport(request)
        if (response.exception != null) {
            throw QueryException("服务端返回错误:${response.exception!!.cause!!.message}", response.exception!!.cause!!)
        }
        val syncProject = Utils.notNull(response.result as? SyncProject, "同步失败：远程服务端返回null")
        // 需要先将项目完全清空
        val defineList = defineService.findByProjectName(targetProject)
        if (Utils.isNotEmpty(defineList)) {
            val defineIdList = defineList!!.map { it.id }.toList()
            defineService.delete(defineList)
            executionObjService.delete(defineIdList)
            projectService.clean(targetProject)
        }
        //插入其他服务端发送的数据
        Utils.notNullAndThen(syncProject.queryDefines) {
            it.forEach { define -> define.userId = userInfoHolder.getUserId() }
            defineService.save(it)
        }
        Utils.notNullAndThen(syncProject.executionObjs) {
            it.forEach { obj -> obj.userId = userInfoHolder.getUserId() }
            executionObjService.save(it)
        }
        Utils.notNullAndThen(syncProject.project2Defines) {
            it.forEach { project2define ->
                project2define.userId = userInfoHolder.getUserId()
                project2define.projectName = targetProject
            }
            project2DefineService.save(it)
        }
    }

    override fun originServerResponse(originProject: String): SyncProject {
        Utils.notNull(projectService.findByName(originProject), "找不到项目：$originProject")
        val project2DefineList = project2DefineService.findByProjectName(originProject)
        return if (Utils.isNotEmpty(project2DefineList)) {
            val defineIdList = project2DefineList!!.map { it.defineId }
            runBlocking {
                val findQueryDefineList = async { findQueryDefineList(defineIdList) }
                val findExecutionObjList = async { findExecutionObjList(defineIdList) }
                return@runBlocking SyncProject(findQueryDefineList.await(), findExecutionObjList.await(), project2DefineList)
            }
        } else {
            SyncProject()
        }
    }

    private suspend fun findQueryDefineList(defineIdList: List<String>): List<QueryDefine>? {
        return defineService.findByIds(defineIdList)
    }

    private suspend fun findExecutionObjList(defineIdList: List<String>): List<ExecutionObj> {
        return executionObjService.findByDefineIds(defineIdList)
    }


    override fun setUserInfoHolder(userInfoHolder: UserInfoHolder) {
        this.userInfoHolder = userInfoHolder
    }
}
